/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"jit/IonIC.h"#include"mozilla/Maybe.h"#include"mozilla/SizePrintfMacros.h"#include"jit/CacheIRCompiler.h"#include"jit/Linker.h"#include"jit/MacroAssembler-inl.h"#include"vm/Interpreter-inl.h"usingnamespacejs;usingnamespacejs::jit;usingmozilla::Maybe;voidIonIC::updateBaseAddress(JitCode*code,MacroAssembler&masm){fallbackLabel_.repoint(code,&masm);rejoinLabel_.repoint(code,&masm);codeRaw_=fallbackLabel_.raw();}RegisterIonIC::scratchRegisterForEntryJump(){switch(kind_){caseCacheKind::GetProp:caseCacheKind::GetElem:{Registertemp=asGetPropertyIC()->maybeTemp();if(temp!=InvalidReg)returntemp;TypedOrValueRegisteroutput=asGetPropertyIC()->output();returnoutput.hasValue()?output.valueReg().scratchReg():output.typedReg().gpr();}caseCacheKind::SetProp:caseCacheKind::SetElem:returnasSetPropertyIC()->temp();caseCacheKind::GetName:returnasGetNameIC()->temp();caseCacheKind::BindName:returnasBindNameIC()->temp();caseCacheKind::In:returnasInIC()->temp();caseCacheKind::HasOwn:returnasHasOwnIC()->output();caseCacheKind::Call:caseCacheKind::TypeOf:caseCacheKind::GetPropSuper:caseCacheKind::GetElemSuper:MOZ_CRASH("Unsupported IC");}MOZ_CRASH("Invalid kind");}voidIonIC::discardStubs(Zone*zone){if(firstStub_&&zone->needsIncrementalBarrier()){// We are removing edges from IonIC to gcthings. Perform one final trace// of the stub for incremental GC, as it must know about those edges.trace(zone->barrierTracer());}#ifdef JS_CRASH_DIAGNOSTICSIonICStub*stub=firstStub_;while(stub){IonICStub*next=stub->next();stub->poison();stub=next;}#endiffirstStub_=nullptr;codeRaw_=fallbackLabel_.raw();state_.trackUnlinkedAllStubs();}voidIonIC::reset(Zone*zone){discardStubs(zone);state_.reset();}voidIonIC::trace(JSTracer*trc){if(script_)TraceManuallyBarrieredEdge(trc,&script_,"IonIC::script_");uint8_t*nextCodeRaw=codeRaw_;for(IonICStub*stub=firstStub_;stub;stub=stub->next()){JitCode*code=JitCode::FromExecutable(nextCodeRaw);TraceManuallyBarrieredEdge(trc,&code,"ion-ic-code");TraceCacheIRStub(trc,stub,stub->stubInfo());nextCodeRaw=stub->nextCodeRaw();}MOZ_ASSERT(nextCodeRaw==fallbackLabel_.raw());}/* static */boolIonGetPropertyIC::update(JSContext*cx,HandleScriptouterScript,IonGetPropertyIC*ic,HandleValueval,HandleValueidVal,MutableHandleValueres){// Override the return value if we are invalidated (bug 728188).IonScript*ionScript=outerScript->ionScript();AutoDetectInvalidationadi(cx,res,ionScript);// If the IC is idempotent, we will redo the op in the interpreter.if(ic->idempotent())adi.disable();if(ic->state().maybeTransition())ic->discardStubs(cx->zone());boolattached=false;if(ic->state().canAttachStub()){// IonBuilder calls PropertyReadNeedsTypeBarrier to determine if it// needs a type barrier. Unfortunately, PropertyReadNeedsTypeBarrier// does not account for getters, so we should only attach a getter// stub if we inserted a type barrier.CanAttachGettercanAttachGetter=ic->monitoredResult()?CanAttachGetter::Yes:CanAttachGetter::No;jsbytecode*pc=ic->idempotent()?nullptr:ic->pc();boolisTemporarilyUnoptimizable=false;GetPropIRGeneratorgen(cx,outerScript,pc,ic->kind(),ic->state().mode(),&isTemporarilyUnoptimizable,val,idVal,val,canAttachGetter);if(ic->idempotent()?gen.tryAttachIdempotentStub():gen.tryAttachStub())ic->attachCacheIRStub(cx,gen.writerRef(),gen.cacheKind(),ionScript,&attached);if(!attached&&!isTemporarilyUnoptimizable)ic->state().trackNotAttached();}if(!attached&&ic->idempotent()){// Invalidate the cache if the property was not found, or was found on// a non-native object. This ensures:// 1) The property read has no observable side-effects.// 2) There's no need to dynamically monitor the return type. This would// be complicated since (due to GVN) there can be multiple pc's// associated with a single idempotent cache.JitSpew(JitSpew_IonIC,"Invalidating from idempotent cache %s:%"PRIuSIZE,outerScript->filename(),outerScript->lineno());outerScript->setInvalidatedIdempotentCache();// Do not re-invalidate if the lookup already caused invalidation.if(outerScript->hasIonScript())Invalidate(cx,outerScript);// We will redo the potentially effectful lookup in Baseline.returntrue;}if(ic->kind()==CacheKind::GetProp){RootedPropertyNamename(cx,idVal.toString()->asAtom().asPropertyName());if(!GetProperty(cx,val,name,res))returnfalse;}else{MOZ_ASSERT(ic->kind()==CacheKind::GetElem);if(!GetElementOperation(cx,JSOp(*ic->pc()),val,idVal,res))returnfalse;}if(!ic->idempotent()){// Monitor changes to cache entry.if(!ic->monitoredResult())TypeScript::Monitor(cx,ic->script(),ic->pc(),res);}returntrue;}/* static */boolIonSetPropertyIC::update(JSContext*cx,HandleScriptouterScript,IonSetPropertyIC*ic,HandleObjectobj,HandleValueidVal,HandleValuerhs){RootedShapeoldShape(cx);RootedObjectGroupoldGroup(cx);IonScript*ionScript=outerScript->ionScript();boolattached=false;boolisTemporarilyUnoptimizable=false;if(ic->state().maybeTransition())ic->discardStubs(cx->zone());if(ic->state().canAttachStub()){oldShape=obj->maybeShape();oldGroup=JSObject::getGroup(cx,obj);if(!oldGroup)returnfalse;if(obj->is<UnboxedPlainObject>()){MOZ_ASSERT(!oldShape);if(UnboxedExpandoObject*expando=obj->as<UnboxedPlainObject>().maybeExpando())oldShape=expando->lastProperty();}RootedValueobjv(cx,ObjectValue(*obj));RootedScriptscript(cx,ic->script());jsbytecode*pc=ic->pc();SetPropIRGeneratorgen(cx,script,pc,ic->kind(),ic->state().mode(),&isTemporarilyUnoptimizable,objv,idVal,rhs,ic->needsTypeBarrier(),ic->guardHoles());if(gen.tryAttachStub()){ic->attachCacheIRStub(cx,gen.writerRef(),gen.cacheKind(),ionScript,&attached,gen.typeCheckInfo());}}jsbytecode*pc=ic->pc();if(ic->kind()==CacheKind::SetElem){if(IsPropertyInitOp(JSOp(*pc))){if(!InitElemOperation(cx,pc,obj,idVal,rhs))returnfalse;}else{MOZ_ASSERT(IsPropertySetOp(JSOp(*pc)));if(!SetObjectElement(cx,obj,idVal,rhs,ic->strict()))returnfalse;}}else{MOZ_ASSERT(ic->kind()==CacheKind::SetProp);if(*pc==JSOP_INITGLEXICAL){RootedScriptscript(cx,ic->script());MOZ_ASSERT(!script->hasNonSyntacticScope());InitGlobalLexicalOperation(cx,&cx->global()->lexicalEnvironment(),script,pc,rhs);}else{RootedPropertyNamename(cx,idVal.toString()->asAtom().asPropertyName());if(!SetProperty(cx,obj,name,rhs,ic->strict(),pc))returnfalse;}}if(attached)returntrue;// The SetProperty call might have entered this IC recursively, so try// to transition.if(ic->state().maybeTransition())ic->discardStubs(cx->zone());if(ic->state().canAttachStub()){RootedValueobjv(cx,ObjectValue(*obj));RootedScriptscript(cx,ic->script());jsbytecode*pc=ic->pc();SetPropIRGeneratorgen(cx,script,pc,ic->kind(),ic->state().mode(),&isTemporarilyUnoptimizable,objv,idVal,rhs,ic->needsTypeBarrier(),ic->guardHoles());if(gen.tryAttachAddSlotStub(oldGroup,oldShape)){ic->attachCacheIRStub(cx,gen.writerRef(),gen.cacheKind(),ionScript,&attached,gen.typeCheckInfo());}else{gen.trackNotAttached();}if(!attached&&!isTemporarilyUnoptimizable)ic->state().trackNotAttached();}returntrue;}/* static */boolIonGetNameIC::update(JSContext*cx,HandleScriptouterScript,IonGetNameIC*ic,HandleObjectenvChain,MutableHandleValueres){IonScript*ionScript=outerScript->ionScript();jsbytecode*pc=ic->pc();RootedPropertyNamename(cx,ic->script()->getName(pc));if(ic->state().maybeTransition())ic->discardStubs(cx->zone());if(ic->state().canAttachStub()){boolattached=false;RootedScriptscript(cx,ic->script());GetNameIRGeneratorgen(cx,script,pc,ic->state().mode(),envChain,name);if(gen.tryAttachStub())ic->attachCacheIRStub(cx,gen.writerRef(),gen.cacheKind(),ionScript,&attached);if(!attached)ic->state().trackNotAttached();}RootedObjectobj(cx);RootedObjectholder(cx);Rooted<PropertyResult>prop(cx);if(!LookupName(cx,name,envChain,&obj,&holder,&prop))returnfalse;if(*GetNextPc(pc)==JSOP_TYPEOF){if(!FetchName<GetNameMode::TypeOf>(cx,obj,holder,name,prop,res))returnfalse;}else{if(!FetchName<GetNameMode::Normal>(cx,obj,holder,name,prop,res))returnfalse;}// No need to call TypeScript::Monitor, IonBuilder always inserts a type// barrier after GetName ICs.returntrue;}/* static */JSObject*IonBindNameIC::update(JSContext*cx,HandleScriptouterScript,IonBindNameIC*ic,HandleObjectenvChain){IonScript*ionScript=outerScript->ionScript();jsbytecode*pc=ic->pc();RootedPropertyNamename(cx,ic->script()->getName(pc));if(ic->state().maybeTransition())ic->discardStubs(cx->zone());if(ic->state().canAttachStub()){boolattached=false;RootedScriptscript(cx,ic->script());BindNameIRGeneratorgen(cx,script,pc,ic->state().mode(),envChain,name);if(gen.tryAttachStub())ic->attachCacheIRStub(cx,gen.writerRef(),gen.cacheKind(),ionScript,&attached);if(!attached)ic->state().trackNotAttached();}RootedObjectholder(cx);if(!LookupNameUnqualified(cx,name,envChain,&holder))returnnullptr;returnholder;}/* static */boolIonHasOwnIC::update(JSContext*cx,HandleScriptouterScript,IonHasOwnIC*ic,HandleValueval,HandleValueidVal,int32_t*res){IonScript*ionScript=outerScript->ionScript();if(ic->state().maybeTransition())ic->discardStubs(cx->zone());jsbytecode*pc=ic->pc();if(ic->state().canAttachStub()){boolattached=false;RootedScriptscript(cx,ic->script());HasPropIRGeneratorgen(cx,script,pc,CacheKind::HasOwn,ic->state().mode(),idVal,val);if(gen.tryAttachStub())ic->attachCacheIRStub(cx,gen.writerRef(),gen.cacheKind(),ionScript,&attached);if(!attached)ic->state().trackNotAttached();}boolfound;if(!HasOwnProperty(cx,val,idVal,&found))returnfalse;*res=found;returntrue;}/* static */boolIonInIC::update(JSContext*cx,HandleScriptouterScript,IonInIC*ic,HandleValuekey,HandleObjectobj,bool*res){IonScript*ionScript=outerScript->ionScript();if(ic->state().maybeTransition())ic->discardStubs(cx->zone());if(ic->state().canAttachStub()){boolattached=false;RootedScriptscript(cx,ic->script());RootedValueobjV(cx,ObjectValue(*obj));jsbytecode*pc=ic->pc();HasPropIRGeneratorgen(cx,script,pc,CacheKind::In,ic->state().mode(),key,objV);if(gen.tryAttachStub())ic->attachCacheIRStub(cx,gen.writerRef(),gen.cacheKind(),ionScript,&attached);if(!attached)ic->state().trackNotAttached();}returnOperatorIn(cx,key,obj,res);}uint8_t*IonICStub::stubDataStart(){returnreinterpret_cast<uint8_t*>(this)+stubInfo_->stubDataOffset();}voidIonIC::attachStub(IonICStub*newStub,JitCode*code){MOZ_ASSERT(newStub);MOZ_ASSERT(code);if(firstStub_){IonICStub*last=firstStub_;while(IonICStub*next=last->next())last=next;last->setNext(newStub,code);}else{firstStub_=newStub;codeRaw_=code->raw();}state_.trackAttached();}